﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Controls;

namespace OpenFrameworkCore.Controls
{
	/// <summary>
	/// OpenFramework에서 제공하는 기본 컨트롤
	/// 
	/// Move, Rotate, Resize, ZIndex 기능을 가지고 있다.
	/// </summary>
	public class BasicControl : OpenFrameworkCore.Controls.ControlBase, Movable, Rotatable, Resizable, TransparentEditable
	{
		#region Transform Members
		protected TranslateTransform translateTransform;
		protected RotateTransform rotateTransform;
		#endregion

		#region X Related Members

		public static readonly DependencyProperty XProperty = DependencyProperty.Register
			(
			"X",
			typeof(double),
			typeof(BasicControl),
			new PropertyMetadata(OnXPropertyChanged)
			);

		private static void OnXPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((BasicControl)d).OnXPropertyChanged(e);
		}

		private void OnXPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			translateTransform.X = (double)e.NewValue;
		}

		/// <summary>
		/// X 좌표
		/// </summary>
		public double X
		{
			get
			{
				return (double)GetValue(XProperty);
			}

			set
			{
				// throw EditablePropertyLocked exception
				if (XEditable == false)
					throw new EditableFieldLockedException("X property can't edit.");

				// Check value whether is less than min value or bigger than max value.
				if (value < XMin)
					value = XMin;

				if (value > XMax)
					value = XMax;

				SetValue(XProperty, value);
			}
		}

		/// <summary>
		/// X 최소값
		/// </summary>
		public double XMin
		{
			get;
			set;
		}

		/// <summary>
		/// X 최대값
		/// </summary>
		public double XMax
		{
			get;
			set;
		}

		/// <summary>
		/// X 좌표 수정 가능 여부
		/// </summary>
		public bool XEditable
		{
			get
			{
				return (bool)GetValue(XEditableProperty);
			}
			set
			{
				SetValue(XEditableProperty, value);
			}
		}

		public static readonly DependencyProperty XEditableProperty = DependencyProperty.Register
			(
			"XEditable",
			typeof(bool),
			typeof(BasicControl),
			null
			);

		#endregion

		#region Y Related Members

		public static readonly DependencyProperty YProperty = DependencyProperty.Register
			(
			"Y",
			typeof(double),
			typeof(BasicControl),
			new PropertyMetadata(OnYPropertyChanged)
			);

		private static void OnYPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((BasicControl)d).OnYPropertyChanged(e);
		}

		private void OnYPropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			translateTransform.Y = (double)e.NewValue;
		}

		/// <summary>
		/// Y 좌표
		/// </summary>
		public double Y
		{
			get
			{
				return (double)GetValue(YProperty);
			}

			set
			{
				// throw EditablePropertyLocked exception
				if (YEditable == false)
					throw new EditableFieldLockedException("Y property can't edit.");

				// Check value whether is less than min value or bigger than max value.
				if (value < YMin)
					value = YMin;

				if (value > YMax)
					value = YMax;

				SetValue(YProperty, value);
			}
		}

		/// <summary>
		/// Y 최소값
		/// </summary>
		public double YMin
		{
			get;
			set;
		}

		/// <summary>
		/// Y 최대값
		/// </summary>
		public double YMax
		{
			get;
			set;
		}

		/// <summary>
		/// Y 좌표 수정 가능 여부
		/// </summary>
		public bool YEditable
		{
			get
			{
				return (bool)GetValue(YEditableProperty);
			}
			set
			{
				SetValue(YEditableProperty, value);
			}
		}

		public static readonly DependencyProperty YEditableProperty = DependencyProperty.Register
			(
			"YEditable",
			typeof(bool),
			typeof(BasicControl),
			null
			);

		#endregion

		#region Angle Related Members

		public static readonly DependencyProperty AngleProperty = DependencyProperty.Register
			(
			"Angle",
			typeof(double),
			typeof(BasicControl),
			new PropertyMetadata(OnAnglePropertyChanged)
			);

		private static void OnAnglePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
		{
			((BasicControl)d).OnAnglePropertyChanged(e);
		}

		private void OnAnglePropertyChanged(DependencyPropertyChangedEventArgs e)
		{
			rotateTransform.Angle = (double)e.NewValue;
		}

		/// <summary>
		/// 기울기
		/// </summary>
		public double Angle
		{
			get
			{
				return (double)GetValue(AngleProperty);
			}

			set
			{
				// throw EditablePropertyLocked exception
				if (AngleEditable == false)
					throw new EditableFieldLockedException("Angle property can't edit.");

				// Check value whether is less than min value or bigger than max value.
				if (value < AngleMin)
					value = AngleMin;

				if (value > AngleMax)
					value = AngleMax;

				SetValue(AngleProperty, value);
			}
		}

		/// <summary>
		/// Angle 최소 값
		/// </summary>
		public double AngleMin
		{
			get;
			set;
		}

		/// <summary>
		/// Angle 최대 값
		/// </summary>
		public double AngleMax
		{
			get;
			set;
		}

		/// <summary>
		/// Angle 수정 가능 여부
		/// </summary>
		public bool AngleEditable
		{
			get
			{
				return (bool)GetValue(AngleEditableProperty);
			}
			set
			{
				SetValue(AngleEditableProperty, value);
			}
		}

		public static readonly DependencyProperty AngleEditableProperty = DependencyProperty.Register
			(
			"AngleEditable",
			typeof(bool),
			typeof(BasicControl),
			null
			);

		#endregion

		#region Width Related Members

		/// <summary>
		/// Width 재정의
		/// </summary>
		public new double Width {
			get
			{
				return base.Width;
			}

			set
			{
				// throw EditablePropertyLocked exception
				if (WidthEditable == false)
					throw new EditableFieldLockedException("Width property can't edit.");

				// Check value whether is less than min value or bigger than max value.
				if (value < WidthMin)
					value = WidthMin;

				if (value > WidthMax)
					value = WidthMax;

				base.Width = value;
			}
		}

		/// <summary>
		/// Width 수정 가능 여부
		/// </summary>
		public bool WidthEditable
		{
			get
			{
				return (bool)GetValue(WidthEditableProperty);
			}
			set
			{
				SetValue(WidthEditableProperty, value);
			}
		}

		public static readonly DependencyProperty WidthEditableProperty = DependencyProperty.Register
			(
			"WidthEditable",
			typeof(bool),
			typeof(BasicControl),
			null
			);

		/// <summary>
		/// Width 최소값
		/// </summary>
		public double WidthMin
		{
			get;
			set;
		}

		/// <summary>
		/// Width 최대값
		/// </summary>
		public double WidthMax
		{
			get;
			set;
		}

		#endregion

		#region Height Related Members
		/// <summary>
		/// Height 재정의
		/// </summary>
		public new double Height
		{
			get
			{
				return base.Height;
			}

			set
			{
				// throw EditablePropertyLocked exception
				if (HeightEditable == false)
					throw new EditableFieldLockedException("Height property can't edit.");

				// Check value whether is less than min value or bigger than max value.
				if (value < HeightMin)
					value = HeightMin;

				if (value > HeightMax)
					value = HeightMax;

				base.Height = value;
			}
		}

		/// <summary>
		/// Height 수정 가능 여부
		/// </summary>
		public bool HeightEditable
		{
			get
			{
				return (bool)GetValue(HeightEditableProperty);
			}
			set
			{
				SetValue(HeightEditableProperty, value);
			}
		}

		public static readonly DependencyProperty HeightEditableProperty = DependencyProperty.Register
			(
			"HeightEditable",
			typeof(bool),
			typeof(BasicControl),
			null
			);

		/// <summary>
		/// Height 최소값
		/// </summary>
		public double HeightMin
		{
			get;
			set;
		}

		/// <summary>
		/// Height 최대값
		/// </summary>
		public double HeightMax
		{
			get;
			set;
		}

		#endregion

		#region Opacity Related Members
		/// <summary>
		/// Opacity 재정의
		/// </summary>
		public new double Opacity
		{
			get
			{
				return base.Opacity;
			}

			set
			{
				// throw EditablePropertyLocked exception
				if (OpacityEditable == false)
					throw new EditableFieldLockedException("Opacity property can't edit.");

				// Check value whether is less than min value or bigger than max value.
				if (value < OpacityMin)
					value = OpacityMin;

				if (value > OpacityMax)
					value = OpacityMax;

				base.Opacity = value;
			}
		}

		/// <summary>
		/// Opacity 수정 가능 여부
		/// </summary>
		public bool OpacityEditable
		{
			get
			{
				return (bool)GetValue(OpacityEditableProperty);
			}
			set
			{
				SetValue(OpacityEditableProperty, value);
			}
		}

		public static readonly DependencyProperty OpacityEditableProperty = DependencyProperty.Register
			(
			"OpacityEditable",
			typeof(bool),
			typeof(BasicControl),
			null
			);

		/// <summary>
		/// Opacity 최소값
		/// </summary>
		public double OpacityMin
		{
			get;
			set;
		}

		/// <summary>
		/// Opacity 최대값
		/// </summary>
		public double OpacityMax
		{
			get;
			set;
		}

		#endregion

		/// <summary>
		/// ControlContext가 변경된 경우에 필요한 Binding을 설치하기 위해 Override 한다.
		/// </summary>
		public override OpenFrameworkCore.Controls.ControlBaseContext ControlContext
		{
			get
			{
				return base.ControlContext;
			}
			set
			{
				base.ControlContext = value;
			}
		}

		/// <summary>
		/// 생성자
		/// </summary>
		public BasicControl() : base()
		{
			// 최소, 최대값 설정
			InitMinMaxValue();

			// Transform 초기화
			InitTransform();
		}

		/// <summary>
		/// Transform 요소를 초기화합니다.
		/// </summary>
		protected virtual void InitTransform()
		{
			translateTransform = new TranslateTransform();
			rotateTransform = new RotateTransform();

			// TransformGroup 생성
			TransformGroup transformGroup = new TransformGroup();

			// Index 0 == TranslateTransform
			transformGroup.Children.Add(translateTransform);

			// Index 1 == RotateTransform
			transformGroup.Children.Add(rotateTransform);

			// Control의 Transform 설정
			RenderTransform = transformGroup;
		}

		/// <summary>
		/// Min, Max Value 초기화
		/// </summary>
		private void InitMinMaxValue()
		{
			XEditable = true;
			XMin = double.MinValue;
			XMax = double.MaxValue;

			YEditable = true;
			YMin = double.MinValue;
			YMax = double.MaxValue;

			AngleEditable = true;
			AngleMin = double.MinValue;
			AngleMax = double.MaxValue;

			WidthEditable = true;
			WidthMin = 10;
			WidthMax = double.MaxValue;

			HeightEditable = true;
			HeightMin = 10;
			HeightMax = double.MaxValue;

			ZIndexEditable = true;
			ZIndexMin = 0;
			ZIndexMax = int.MaxValue;

			OpacityEditable = true;
			OpacityMin = 0.0;
			OpacityMax = 1.0;
		}

		/// <summary>
		/// Binding을 초기화 합니다.
		/// </summary>
		protected override void InitBinding()
		{
			base.InitBinding();

			// Width binding 설정
			Binding widthBinding = new Binding("Width");
			widthBinding.Source = ControlContext;
			widthBinding.Mode = BindingMode.TwoWay;
			SetBinding(WidthProperty, widthBinding);

			// Height binding 설정
			Binding heightBinding = new Binding("Height");
			heightBinding.Source = ControlContext;
			heightBinding.Mode = BindingMode.TwoWay;
			SetBinding(HeightProperty, heightBinding);

			// X binding 설정
			Binding xBinding = new Binding("X");
			xBinding.Source = ControlContext;
			xBinding.Mode = BindingMode.TwoWay;
			SetBinding(XProperty, xBinding);

			// Y binding 설정
			Binding yBinding = new Binding("Y");
			yBinding.Source = ControlContext;
			yBinding.Mode = BindingMode.TwoWay;
			SetBinding(YProperty, yBinding);

			// Angle binding 설정
			Binding angleBinding = new Binding("Angle");
			angleBinding.Source = ControlContext;
			angleBinding.Mode = BindingMode.TwoWay;
			SetBinding(AngleProperty, angleBinding);

			// Opacity binding 설정
			Binding opacityBinding = new Binding("Opacity");
			opacityBinding.Source = ControlContext;
			opacityBinding.Mode = BindingMode.TwoWay;
			SetBinding(OpacityProperty, opacityBinding);
		}

		/// <summary>
		/// Binding을 제거합니다.
		/// </summary>
		protected override void ClearBinding()
		{
			base.ClearBinding();

			ClearValue(WidthProperty);
			ClearValue(HeightProperty);
			ClearValue(XProperty);
			ClearValue(YProperty);
			ClearValue(AngleProperty);
			ClearValue(OpacityProperty);
		}
	}
}
